/* Copyright 2007-2008 dnAnalytics Project.
 *
 * Contributors to this file:
 * Patrick van der Velde
 * Marcus Cuda
 * Jurgen Van Gael
 *
 * This file is part of dnAnalytics.  dnAnalytics is licensed under the 
 * Microsoft Public License. See License.txt for a complete copy of the
 * license.
 */

using System;
using dnAnalytics.Properties;

namespace dnAnalytics.LinearAlgebra
{
    /// <summary>
    /// A vector class using sparse storage.
    /// </summary>
    [Serializable]
    public partial class SparseVector : Vector
    {
        /// <summary>
        /// Creates instance of the <see cref="SparseVector" /> class.
        /// </summary>
        /// <param name="size">The size of the vector.</param>
        /// <exception cref="ArgumentException">If the size of the vector is smaller than 1.</exception>
        public SparseVector(int size) : base(size) { }

        /// <summary>
        /// Creates instance of the <see cref="SparseVector" /> class.
        /// </summary>
        /// <param name="values">The values to fill the vector with.</param>
        /// <exception cref="ArgumentNullException">If the values array is <b>null</b>.</exception>
        public SparseVector(double[] values) : base(values.Length)
        {
            for (int i = 0; i < values.Length; i++)
            {
                InsertValue(i, values[i]);
            }
        }

        /// <summary>
        /// Creates instance of the <see cref="SparseVector" /> class.
        /// </summary>
        /// <param name="source">The <see cref="Vector" /> from which the values for the current instance will be copied.</param>
        /// <exception cref="ArgumentNullException">If source is <b>null</b>.</exception>
        public SparseVector(Vector source) : base(source.Count)
        {
            for (int i = 0; i < source.Count; i++)
            {
                InsertValue(i, source[i]);
            }
        }

        /// <summary>
        /// Creates a <strong>SparseVector</strong> of the given size and with each element set the given value.
        /// </summary>
        /// <param name="size">the size of the vector.</param> 
        /// <param name="value">the value to set each element to.</param>
        /// <exception cref="ArgumentException">If <paramref name="size"/> is less than one.</exception>
        public SparseVector(int size, double value)
            : this(size)
        {
            for (int i = 0; i < size; i++)
            {
                InsertValue(i, value);
            }
        }

        /// <summary>
        /// Creates a <strong>SparseVector</strong> of the given size.
        /// </summary>
        /// <param name="size">The size of the <strong>SparseVector</strong> to create.</param>
        /// <returns>The new <strong>Vector</strong>.</returns>
        protected internal override Vector CreateVector(int size)
        {
            return new SparseVector(size);
        }

        /// <summary>
        /// Creates a matrix with the given dimensions using the same storage type
        /// as this vector uses.
        /// </summary>
        /// <param name="rows">The number of rows.</param>
        /// <param name="columns">The number of columns.</param>
        /// <returns>A matrix with the given dimensions.</returns>
        public override Matrix CreateMatrix(int rows, int columns)
        {
            return new SparseMatrix(rows, columns);
        }

        /// <summary>
        /// Returns a <strong>Vector</strong> containing the same values of rightSide. 
        /// </summary>
        /// <remarks>This method is included for completeness.</remarks>
        /// <param name="rightSide">The vector to get the values from.</param>
        /// <returns>A vector containing a the same values as <paramref name="rightSide"/>.</returns>
        /// <exception cref="ArgumentNullException">If <paramref name="rightSide"/> is <see langword="null" />.</exception>
        public static SparseVector operator +(SparseVector rightSide)
        {
            if (rightSide == null)
            {
                throw new ArgumentNullException("rightSide");
            }
            return new SparseVector(rightSide);
        }

        /// <summary>
        /// Adds two <strong>Vectors</strong> together and returns the results.
        /// </summary>
        /// <param name="leftSide">One of the vectors to add.</param>
        /// <param name="rightSide">One of the vectors to add.</param>
        /// <returns>The result of the addition.</returns>
        /// <exception cref="NotConformableException">If <paramref name="leftSide"/> and <paramref name="rightSide"/> are not the same size.</exception>
        /// <exception cref="ArgumentNullException">If <paramref name="leftSide"/> or <paramref name="rightSide"/> is <see langword="null" />.</exception>
        public static SparseVector operator +(SparseVector leftSide, SparseVector rightSide)
        {
            if (rightSide == null)
            {
                throw new ArgumentNullException("rightSide");
            }
            if (leftSide == null)
            {
                throw new ArgumentNullException("leftSide");
            }

            if (leftSide.Count != rightSide.Count)
            {
                throw new NotConformableException(Resources.ParametersNotConformable);
            }

            SparseVector ret = new SparseVector(leftSide);
            ret.Add(rightSide);
            return ret;
        }

        /// <summary>
        /// Negates each element of the vector.
        /// </summary>
        /// <param name="rightSide">The vector to negate.</param>
        /// <returns>A vector containing the negated values.</returns>
        /// <exception cref="ArgumentNullException">If <paramref name="rightSide"/> is <see langword="null" />.</exception>
        public static SparseVector operator -(SparseVector rightSide)
        {
            if (rightSide == null)
            {
                throw new ArgumentNullException("rightSide");
            }

            SparseVector ret = new SparseVector(rightSide);
            ret.Negate();
            return ret;
        }

        /// <summary>
        /// Subtracts two vectors and returns the results.
        /// </summary>
        /// <param name="leftSide">The vector to subtract from.</param>
        /// <param name="rightSide">The vector to subtract.</param>
        /// <returns>The result of the subtraction.</returns>
        /// <exception cref="NotConformableException">If <paramref name="leftSide"/> and <paramref name="rightSide"/> are not the same size.</exception>
        /// <exception cref="ArgumentNullException">If <paramref name="leftSide"/> or <paramref name="rightSide"/> is <see langword="null" />.</exception>
        public static SparseVector operator -(SparseVector leftSide, SparseVector rightSide)
        {
            if (rightSide == null)
            {
                throw new ArgumentNullException("rightSide");
            }
            if (leftSide == null)
            {
                throw new ArgumentNullException("leftSide");
            }

            if (leftSide.Count != rightSide.Count)
            {
                throw new NotConformableException(Resources.ParametersNotConformable);
            }
            SparseVector ret = new SparseVector(leftSide);
            ret.Subtract(rightSide);
            return ret;
        }

        /// <summary>
        /// Divides a <strong>Vector</strong> by a constant and returns the result.
        /// </summary>
        /// <param name="leftSide">The vector to divide.</param>
        /// <param name="rightSide">The constant to divide the vector by.</param>
        /// <returns>The result of the division.</returns>
        /// <exception cref="ArgumentNullException">If <paramref name="leftSide"/> is <see langword="null" />.</exception>
        public static SparseVector operator /(SparseVector leftSide, double rightSide)
        {
            if (leftSide == null)
            {
                throw new ArgumentNullException("leftSide");
            }

            SparseVector ret = new SparseVector(leftSide);
            ret.Divide(rightSide);
            return ret;
        }

        /// <summary>
        /// Multiplies a <strong>Vector</strong> by a constant and returns the result.
        /// </summary>
        /// <param name="leftSide">The vector to multiply.</param>
        /// <param name="rightSide">The constant to multiply the vector by.</param>
        /// <returns>The result of the multiplication.</returns>
        /// <exception cref="ArgumentNullException">If <paramref name="rightSide"/> is <see langword="null" />.</exception>
        public static SparseVector operator *(double leftSide, SparseVector rightSide)
        {
            if (rightSide == null)
            {
                throw new ArgumentNullException("rightSide");
            }
            SparseVector ret = new SparseVector(rightSide);
            ret.Multiply(leftSide);
            return ret;
        }

        /// <summary>
        /// Multiplies a <strong>Vector</strong> by a constant and returns the result.
        /// </summary>
        /// <param name="leftSide">The vector to multiply.</param>
        /// <param name="rightSide">The constant to multiply the vector by.</param>
        /// <returns>The result of the multiplication.</returns>
        /// <exception cref="ArgumentNullException">If <paramref name="leftSide"/> is <see langword="null" />.</exception>
        public static SparseVector operator *(SparseVector leftSide, double rightSide)
        {
            if (leftSide == null)
            {
                throw new ArgumentNullException("leftSide");
            }
            SparseVector ret = new SparseVector(leftSide);
            ret.Multiply(rightSide);
            return ret;
        }

        /// <summary>
        /// Multiples two <strong>Vectors</strong>. The first vector is treated as column vector and
        /// the second as row vector.
        /// </summary>
        /// <param name="leftSide">One of the vectors to multiply.</param>
        /// <param name="rightSide">One of the vectors to multiply.</param>
        /// <returns>The result of multiplication.</returns>
        /// <exception cref="ArgumentNullException">If <paramref name="leftSide"/> or <paramref name="rightSide"/> is <see langword="null" />.</exception>
        public static Matrix operator *(SparseVector leftSide, SparseVector rightSide)
        {
            if (rightSide == null)
            {
                throw new ArgumentNullException("rightSide");
            }

            if (leftSide == null)
            {
                throw new ArgumentNullException("leftSide");
            }
            return leftSide.Multiply(rightSide);
        }
    }
}
